#!/bin/sh
# 60-macbook - Battery Plugin for Apple Silicon Macbooks w/ applesmc driver
#
# Copyright (c) 2024 Thomas Koch <linrunner at gmx.net> and others.
# SPDX-License-Identifier: GPL-2.0-or-later

# Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat

# --- Hardware Detection

readonly BATDRV_APPLESMC_MD=/sys/class/power_supply/macsmc-battery

batdrv_is_applesmc () {
    # check if vendor specific kernel module is loaded
    # rc: 0=ok, 1=other hardware
    [ -d $BATDRV_APPLESMC_MD ]
}

# --- Plugin API functions

batdrv_init () {
    # detect hardware and initialize driver
    # rc: 0=matching hardware detected/1=not detected/2=no batteries detected
    # retval: $_batdrv_plugin, $_batdrv_kmod
    #
    # 1. check for native kernel acpi (Linux 5.4 or higher required)
    #    --> retval $_natacpi:
    #       0=thresholds/
    #       32=disabled/
    #       128=no kernel support/
    #       254=laptop not supported
    #
    # 2. determine method for
    #    reading battery data                   --> retval $_bm_read,
    #    reading/writing charging thresholds    --> retval $_bm_thresh,
    #    reading/writing force discharge        --> retval $_bm_dischg:
    #       none/natacpi
    #
    # 3. define sysfile basenames for natacpi
    #    start threshold                        --> retval $_bn_start,
    #    stop threshold                         --> retval $_bn_stop,
    #    discharge                              --> retval $_bn_discharge
    #
    # 4. determine present batteries
    #    list of batteries (space separated)    --> retval $_batteries;
    #
    # 5. define charge threshold defaults
    #    start threshold                        --> retval $_bt_def_start,
    #    stop threshold                         --> retval $_bt_def_stop;

    _batdrv_plugin="macbook"
    _batdrv_kmod="macsmc_power" # kernel module for natacpi (Asahi Linux)

    # check plugin simulation override and denylist
    if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then
        if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then
            echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate"
        else
            echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip"
            return 1
        fi
    elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then
        echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist"
        return 1
    else
        # check if hardware matches
        if ! batdrv_is_applesmc; then
            echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match"
            return 1
        fi
    fi

    # presume no features at all
    _natacpi=128
    # shellcheck disable=SC2034
    _bm_read="natacpi"
    _bm_thresh="none"
    _bm_dischg="none"
    _bn_start=""
    _bn_stop=""
    _bn_dischg=""
    _batteries=""
    _bt_def_start=100
    _bt_def_stop=100

    # iterate batteries and check for native kernel ACPI
    # note: assume only one battery called "macsmc-battery"
    local bd bs
    local done=0
    local bat_glob="macsmc-battery"
    if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then
        bat_glob='BAT[01]'
    fi
    for bd in "$ACPIBATDIR"/$bat_glob; do
        if [ "$(read_sysf "$bd/present")" = "1" ]; then
            # record detected batteries and directories
            bs=${bd##/*/}
            if [ -n "$_batteries" ]; then
                _batteries="$_batteries $bs"
            else
                _batteries="$bs"
            fi
            # skip natacpi detection for 2nd and subsequent batteries
            [ $done -eq 1 ] && continue

            done=1
            if [ "$NATACPI_ENABLE" = "0" ]; then
                # natacpi disabled in configuration --> skip actual detection
                _natacpi=32
                continue
            fi

            if [ -f "$bd/charge_control_start_threshold" ] \
                && [ -f "$bd/charge_control_end_threshold" ]; then
                # threshold sysfiles exist
                _bn_start="charge_control_start_threshold"
                _bn_stop="charge_control_end_threshold"
                _natacpi=254
            else
                # nothing detected
                _natacpi=254
                continue
            fi

            if readable_sysf "$bd/$_bn_start" \
                && readable_sysf "$bd/$_bn_stop"; then
                # threshold sysfiles are actually readable
                _natacpi=0
                _bm_thresh="natacpi"

                if readable_sysf "$bd/charge_behaviour"; then
                    # sysfile for force-discharge exists and is actually readable
                    _bm_dischg="natacpi"
                    _bn_dischg="charge_behaviour"
                fi
            fi
        fi
    done

    # quit if no battery detected, there is no point in activating the plugin
    if [ -z "$_batteries" ]; then
        echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_batteries"
        return 2
    fi

    # shellcheck disable=SC2034
    _batdrv_selected=$_batdrv_plugin
    echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bn_start=$_bn_start; bn_stop=$_bn_stop; dischg=$_bm_dischg; bn_dischg=$_bn_dischg"
    return 0
}

batdrv_select_battery () {
    # determine battery acpidir and sysfiles
    # $1: macsmc-battery/DEF
    # global params: $_batdrv_plugin, $_batteries, $_bn_start, $_bn_stop, $_bn_dischg
    # rc: 0=bat exists/1=bat non-existent
    # retval: $_bat_str:    macsmc-battery;
    #         $_bt_cfg_bat: config suffix (BAT0);
    #         $_bd_read:    directory with battery data sysfiles;
    #         $_bf_start:   sysfile for start threshold;
    #         $_bf_stop:    sysfile for stop threshold;
    #         $_bf_dischg:  sysfile for force-discharge;

    # prerequisite: batdrv_init()

    # defaults
    _bat_str=""    # no bat
    _bt_cfg_bat=""
    _bd_read=""    # no directory
    _bf_start=""
    _bf_stop=""
    _bf_dischg=""

    local bat="$1"

    # convert battery param to lowercase for backward compatibility
    # with versions earlier than 1.7, but not in simulation
    if [ -z "$X_BAT_PLUGIN_SIMULATE" ]; then
        bat="$(printf '%s' "$bat" | tr "[:upper:]" "[:lower:]")"
    fi

    # validate battery param
    case "$bat" in
        DEF|def) # 1st battery is default
            _bat_str="${_batteries%% *}"
            ;;

        *)
            if wordinlist "$bat" "$_batteries"; then
                _bat_str="$bat"
            else
                # battery not present --> quit
                echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present"
                return 1
            fi
            ;;
    esac

    # one battery only -> always use BAT0 config
    _bt_cfg_bat="BAT0"

    # determine natacpi sysfiles
    _bd_read="$ACPIBATDIR/$_bat_str"
    if [ "$_bm_thresh" = "natacpi" ]; then
        _bf_start="$ACPIBATDIR/$_bat_str/$_bn_start"
        _bf_stop="$ACPIBATDIR/$_bat_str/$_bn_stop"
    fi

    if [ "$_bm_dischg" = "natacpi" ]; then
        _bf_dischg="$ACPIBATDIR/$_bat_str/$_bn_dischg"
    fi

    echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; cfg=$_bt_cfg_bat; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop; bf_dischg=$_bf_dischg"
    return 0
}

batdrv_read_threshold () {
    # read and print charge threshold
    # $1: start/stop
    # $2: 0=api/1=tlp-stat output
    # global params: $_batdrv_plugin, $_bm_thresh, $_bf_start, $_bf_stop
    # out:
    # - api: 0..100/"" on error
    # - tlp-stat: 0..100/"(not available)" on error
    # rc: 0=ok/4=read error/255=no api
    # prerequisite: batdrv_init(), batdrv_select_battery()

    local bf out="" rc=0

    case $1 in
        start) out="$X_THRESH_SIMULATE_START" ;;
        stop)  out="$X_THRESH_SIMULATE_STOP"  ;;
    esac
    if [ -n "$out" ]; then
        printf "%s" "$out"
        echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1, $2).simulate: bm_thresh=$_bm_thresh; bf=$bf; out=$out; rc=$rc"
        return 0
    fi

    if [ "$_bm_thresh" = "natacpi" ]; then
        # read threshold from sysfile
        case $1 in
            start) bf=$_bf_start ;;
            stop)  bf=$_bf_stop  ;;
        esac
        if ! out=$(read_sysf "$bf"); then
            # not readable/non-existent
            if [ "$2" != "1" ]; then
                out=""
            else
                out="(not available)"
            fi
            rc=4
        fi
    else
        # no threshold api
        if [ "$2" = "1" ]; then
            out="(not available)"
        fi
        rc=255
    fi

    # "return" threshold
    if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then
        printf "%s" "$out"
    else
        if [ "$2" = "1" ]; then
            printf "(not available)\n"
        fi
        rc=4
    fi

    echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1, $2): bm_thresh=$_bm_thresh; bf=$bf; out=$out; rc=$rc"
    return $rc
}

batdrv_write_thresholds () {
    # write both charge thresholds for a battery
    # use pre-determined method and sysfiles from global parms
    # notes:
    # * Apple Silicon hardware with MacOS 13.0 or later firmware provides only two discrete threshold value pairs:
    #   100/100 or 75/80
    # * The kernel driver discards writes to the start threshold; upon reading, it returns a discrete value that
    #   matches the stop threshold
    # $1: new start threshold -- unused dummy for plugin api compatibility
    # $2: new stop threshold 80/100/DEF(default)
    # $3: 0=quiet/1=output parameter errors/2=output progress and errors
    # $4: non-empty string indicates thresholds stem from configuration
    # global params: $_batdrv_plugin, $_bm_thresh, $_bat_str, $_bt_cfg_bat, $_bf_start, $_bf_stop, $_bt_def_start, $_bt_def_stop,
    # rc: 0=ok/
    #     1=not configured/
    #     2=threshold(s) out of range or non-numeric/
    #     3=minimum start stop diff violated/
    #     4=threshold read error/
    #     5=threshold write error
    # prerequisite: batdrv_init(), batdrv_select_battery()
    local new_start=${1:-}
    local new_stop=${2:-}
    local verb=${3:-0}
    local old_stop

    # insert defaults
    [ "$new_start" = "DEF" ] && new_start=$_bt_def_start
    [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop

    # --- validate thresholds
    if [ -n "$4" ] && [ -z "$new_start" ] && [ -z "$new_stop" ]; then
        # do nothing if unconfigured
        echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).not_configured: bat=$_bat_str; cfg=$_bt_cfg_bat"
        return 1
    fi

    # stop: check for 3 digits max, ensure 80 or 100
    if ! is_uint "$new_stop" 3 || ! wordinlist "$new_stop" "80 100"; then
        # threshold out of range
        echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop: bat=$_bat_str; cfg=$_bt_cfg_bat"
        case $verb in
            1)
                if [ -n "$4" ]; then
                    echo_message "Error in configuration at STOP_CHARGE_THRESH_${_bt_cfg_bat}=\"${new_stop}\": not specified or invalid (must be 80 or 100). Battery skipped."
                fi
                ;;

            2)
                if [ -n "$4" ]; then
                    cprintf "" "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified or invalid (must be 80 or 100). Aborted.\n" "$_bt_cfg_bat" "$new_stop" 1>&2
                else
                    cprintf "" "Error: stop charge threshold (%s) for %s is not specified or invalid (must be 80 or 100). Aborted.\n" "$new_stop" "$_bat_str" 1>&2
                fi
                ;;
         esac
         return 2
    fi

    # start threshold value depends on stop threshold (hardware constraint)
    case "$new_stop" in
        80) new_start="75" ;;
        100) new_start="100" ;;
    esac

    # read active stop threshold value
    if ! old_stop=$(batdrv_read_threshold stop 0); then
        echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).read_error: bat=$_bat_str; cfg=$_bt_cfg_bat"
        case $verb in
            1) echo_message "Error: could not read current stop charge threshold for $_bat_str. Battery skipped." ;;
            2) cprintf "" "Error: could not read current stop charge threshold for %s. Aborted.\n" "$_bat_str" 1>&2 ;;
        esac
        return 4
    fi

    # write new stop threshold
    if [ "$verb" = "2" ]; then
        printf "Setting temporary charge thresholds for %s:\n" "$_bat_str" 1>&2
    fi

    local rc=0
    if [ "$old_stop" != "$new_stop" ]; then
        # new threshold differs from effective one --> write it
        write_sysf "$new_stop" "$_bf_stop" || rc=5
        echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).stop.write: bat=$_bat_str; cfg=$_bt_cfg_bat; old=$old_stop; new=$new_stop; rc=$rc"

        case $verb in
            2)
                if [ $rc -eq 0 ]; then
                    printf        "  %-5s = %3d\n" "stop" "$new_stop" 1>&2
                    printf        "  %-5s = %3d (due to hardware constraint)\n" "start" "$new_start" 1>&2
                else
                    cprintf "err" "  %-5s = %3d (Error: write failed)\n" "stop" "$new_stop" 1>&2
                fi
                ;;
            1)
                if [ $rc -gt 0 ]; then
                    echo_message "Error: writing stop charge threshold for $_bat_str failed."
                fi
                ;;
        esac
    else
        echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).stop.no_change: bat=$_bat_str; cfg=$_bt_cfg_bat; old=$old_stop; new=$new_stop"

        if [ "$verb" = "2" ]; then
            printf "  %-5s = %3d (no change)\n" "stop" "$new_stop" 1>&2
            printf "  %-5s = %3d (no change)\n" "start" "$new_start" 1>&2
        fi
    fi

    if [ "$rc" -eq 0 ] && [ "$verb" = "2" ]; then
        soc_gt_stop_notice
    fi

    echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).complete: bat=$_bat_str; cfg=$_bt_cfg_bat; rc=$rc"
    return $rc
}

batdrv_chargeonce () {
    # function not implemented
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented"
    return 255
}

batdrv_apply_configured_thresholds () {
    # apply configured stop thresholds from configuration to all batteries
    # - called for bg tasks tlp init [re]start/auto and tlp start
    # output parameter errors only
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then
        if batdrv_select_battery BAT0; then
            batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 1; rc=$?
        fi
    else
        if batdrv_select_battery macsmc-battery; then
            batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 1; rc=$?
        fi
    fi
    return 0
}

batdrv_read_force_discharge () {
    # read and print force-discharge state
    # $1: 0=api/1=tlp-stat output
    # global params: $_batdrv_plugin, $_bm_dischg, $_bat_str, $_bf_dischg
    # out:
    # - api: 0=off/1=on/"" on error
    # - tlp-stat: status text/"(not available)" on error
    # rc: 0=ok/4=read error/255=no api
    # prerequisite: batdrv_init(), batdrv_select_battery()

    local rc=0 out=""

    if [ "$_bm_dischg" = "natacpi" ]; then
        # read state from sysfile
        if out=$(read_sysf "$_bf_dischg"); then
            if [ "$1" != "1" ]; then
                if echo "$out" | grep -q "\[force-discharge\]"; then
                    out=1
                else
                    out=0
                fi
            fi
        else
            # not readable/non-existent
            if [ "$1" != "1" ]; then
                out=""
            else
                out="(not available)"
            fi
            rc=4
        fi
    else
        # no discharge api
        if [ "$1" = "1" ]; then
            out="(not available)"
        fi
        rc=255
    fi
    printf "%s" "$out"

    if [ "$rc" -gt 0 ]; then
        # log output in the error case only
        echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge($_bat_str): bm_dischg=$_bm_dischg; bf_dischg=$_bf_dischg; out=$out; rc=$rc"
    fi
    return $rc

}

batdrv_write_force_discharge () {
    # function not implemented for Macbooks
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented"
    return 255
}

batdrv_cancel_force_discharge () {
    # function not implemented for Macbooks
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented"
    return 255
}

batdrv_force_discharge_active () {
    # function not implemented for Macbooks
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented"
    return 255
}

batdrv_discharge () {
    # function not implemented for Macbooks
    # global param: $_batdrv_plugin
    # prerequisite: batdrv_init()

    # Important: release lock from caller
    unlock_tlp tlp_discharge

    echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented"
    return 255
}

batdrv_show_battery_data () {
    # output battery status
    # $1: 1=verbose
    # global params: $_batdrv_plugin, $_batteries, $_batdrv_kmod, $_natacpi, $_bm_thresh, $_bd_read, $_bf_start, $_bf_stop, $_bf_dischg
    # prerequisite: batdrv_init()

    local verbose=${1:-0}

    printf "+++ Battery Care\n"
    printf "Plugin: %s\n" "$_batdrv_plugin"

    if [ "$_bm_thresh" != "none" ]; then
        cprintf "success" "Supported features: charge thresholds\n"
    else
        cprintf "warning" "Supported features: none available\n"
    fi

    printf "Driver usage:\n"
    # native kernel ACPI battery API
    case $_natacpi in
        0)   printf "* natacpi (%s) = active (charge thresholds)\n" "$_batdrv_kmod" ;;
        32)  printf "* natacpi (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;;
        128) printf "* natacpi (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;;
        254) printf "* natacpi (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;;
        *)   printf "* natacpi (%s) = unknown status\n" "$_batdrv_kmod" ;;
    esac
    if [ "$_bm_thresh" != "none" ]; then
        printf "Parameter value ranges:\n"
        printf "* START_CHARGE_THRESH_BAT0:  don't care (hardware enforces 75, 100)\n"
        printf "* STOP_CHARGE_THRESH_BAT0:   80, 100(default)\n"
    fi
    printf "\n"

    # -- show battery data
    local bat
    local bcnt=0
    local ed ef en
    local efsum=0
    local ensum=0

    for bat in $_batteries; do # iterate batteries
        batdrv_select_battery "$bat"

        printf "+++ Battery Status: %s\n" "$bat"

        printparm "%-59s = ##%s##" "$_bd_read/manufacturer"
        printparm "%-59s = ##%s##" "$_bd_read/model_name"

        print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf "$_bd_read/cycle_count")"

        if [ -f "$_bd_read/energy_full" ]; then
            printparm "%-59s = ##%6d## [mWh]" "$_bd_read/energy_full_design" "" 000
            printparm "%-59s = ##%6d## [mWh]" "$_bd_read/energy_full" "" 000
            printparm "%-59s = ##%6d## [mWh]" "$_bd_read/energy_now" "" 000
            printparm "%-59s = ##%6d## [mW]" "$_bd_read/power_now" "" 000

            # store values for charge / capacity calculation below
            ed=$(read_sysval "$_bd_read/energy_full_design")
            ef=$(read_sysval "$_bd_read/energy_full")
            en=$(read_sysval "$_bd_read/energy_now")
            efsum=$((efsum + ef))
            ensum=$((ensum + en))

        elif [ -f "$_bd_read/charge_full" ]; then
            printparm "%-59s = ##%6d## [mAh]" "$_bd_read/charge_full_design" "" 000
            printparm "%-59s = ##%6d## [mAh]" "$_bd_read/charge_full" "" 000
            printparm "%-59s = ##%6d## [mAh]" "$_bd_read/charge_now" "" 000
            printparm "%-59s = ##%6d## [mA]" "$_bd_read/current_now" "" 000

            # store values for charge / capacity calculation below
            ed=$(read_sysval "$_bd_read/charge_full_design")
            ef=$(read_sysval "$_bd_read/charge_full")
            en=$(read_sysval "$_bd_read/charge_now")
            efsum=$((efsum + ef))
            ensum=$((ensum + en))

        else
            ed=0
            ef=0
            en=0
        fi

        print_batstate "$_bd_read/status"
        printf "\n"

        if [ "$verbose" -eq 1 ]; then
            printparm "%-59s = ##%6s## [mV]" "$_bd_read/voltage_min_design" "" 000
            printparm "%-59s = ##%6s## [mV]" "$_bd_read/voltage_now" "" 000
            printf "\n"
        fi

        # --- show battery features: thresholds, force_discharge
        local lf=0
        if [ "$_bm_thresh" = "natacpi" ]; then
            printf "%-69s = %6s [%%]\n" "$_bf_start" "$(batdrv_read_threshold start 1)"
            printf "%-69s = %6s [%%]\n" "$_bf_stop"  "$(batdrv_read_threshold stop 1)"
            lf=1
        fi
        if [ "$_bm_dischg" = "natacpi" ]; then
            printf "%-69s = %6s\n" "$_bf_dischg" "$(batdrv_read_force_discharge 1)"
            lf=1
        fi
        [ $lf -gt 0 ] && printf "\n"

        # --- show charge level (SOC) and capacity
        lf=0
        if [ "$ef" -ne 0 ]; then
            perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge",   100.0 * '"$en"' / '"$ef"');'
            lf=1
        fi
        if [ "$ed" -ne 0 ]; then
            perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '"$ef"' / '"$ed"');'
            lf=1
        fi
        [ "$lf" -gt 0 ] && printf "\n"

        bcnt=$((bcnt+1))

    done # for bat

    if [ $bcnt -gt 1 ] && [ $efsum -ne 0 ]; then
        # more than one battery detected --> show charge total
        perl -e 'printf ("%-59s = %6.1f [%%]\n", "+++ Charge total",   100.0 * '"$ensum"' / '"$efsum"');'
        printf "\n"
    fi

    return 0
}

batdrv_check_soc_gt_stop () {
    # check if battery charge level (SOC) is greater than the stop threshold
    # rc: 0=greater/¹=less or equal (or thresholds not supported)
    # global params: $_bm_thresh, $_bat_str
    # prerequisite: batdrv_init(), batdrv_select_battery()

    local soc stop

    if [ "$_bm_thresh" = "natacpi" ] && soc=$(read_sysval "$ACPIBATDIR/$_bat_str/capacity"); then
        stop="$(batdrv_read_threshold stop 0)"
        if [ -n "$stop" ] && [ "$soc" -gt "$stop" ]; then
            return 0
        fi
    fi

    return 1
}

batdrv_recommendations () {
    # output Macbook specific recommendations
    # prerequisite: batdrv_init()

    soc_gt_stop_recommendation

    return 0
}
